using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace stsdev {

    public class ManifestBuilder {

        private const string nsSP = "http://schemas.microsoft.com/sharepoint/";

        public static void Create(string FilePath, bool AssemblyDeployment) {
            Create(FilePath, AssemblyDeployment, SolutionBuilder.ResetWebServer);
        }

        public static void Create(string FilePath, bool AssemblyDeployment, bool IISRESET) {

            // Added 081612 wictor - check if the file is read-only, if so the issue a warning that fits into Visual Studio Errors List
            FileInfo fileInfo = new FileInfo(FilePath);
            if (fileInfo.Exists && fileInfo.IsReadOnly) {
                Console.WriteLine(string.Format("{0}: warning : Unable to update the Manifest file. File is read-only", FilePath));
                return;
            }
            // End 080612 Addition
            // Modification 080612 wictor - added using statement to the StreamWriter
            using (StreamWriter manifestStream = new StreamWriter(FilePath)) {
                XmlWriterSettings settings = new XmlWriterSettings();
                settings.Encoding = Encoding.UTF8;
                settings.Indent = true;
                settings.NewLineOnAttributes = false;
                settings.NewLineHandling = NewLineHandling.None;
                XmlWriter writer = XmlWriter.Create(manifestStream, settings);
                writer.WriteStartDocument();

                writer.WriteComment("Manifest created STSDEV utility at " + DateTime.Now.ToString());
                writer.WriteStartElement("Solution", nsSP);
                // add solution ID
                writer.WriteAttributeString("SolutionId", SolutionBuilder.SolutionId);
                writer.WriteAttributeString("ResetWebServer", IISRESET.ToString());

                // Modified 20090722 - bphillips - Changed to search FEATURES folder for feature.xml files.
                if (Directory.Exists(Globals.FeaturesFolder)) {
                    writer.WriteComment("Feature Manifest files");
                    writer.WriteStartElement("FeatureManifests");
                    WriteFeatureManifestFiles(writer);
                    writer.WriteEndElement(); // </FeatureManifests> 
                }
                // End of Modified 20090722

                if (Directory.Exists(Globals.TemplatesFolder)) {
                    // write TEMPLATE Files
                    writer.WriteComment("TEMPLATE files");
                    writer.WriteStartElement("TemplateFiles");
                    WriteTemplateFiles(writer);
                    writer.WriteEndElement(); // </TemplateFiles>
                }

                if (Directory.Exists(Globals.IsapiFolder) || Directory.Exists(Globals.ResourcesFolder)) {
                    // write Root Files
                    writer.WriteComment("Root files");
                    writer.WriteStartElement("RootFiles");
                    WriteRootFiles(writer);
                    writer.WriteEndElement(); // </RootFiles>
                }

                if (SolutionBuilder.AssemblyDeployment) {
                    // write Assembly Files
                    writer.WriteComment("Assembly files");
                    writer.WriteStartElement("Assemblies");
                    foreach (SolutionAssembly asm in SolutionBuilder.Assemblies) {
                        WriteAssembly(writer, asm);
                    }
                    writer.WriteEndElement(); // </Assemblies>
                }

                if ((SolutionBuilder.CasPermissions) &&
                     (SolutionBuilder.CasPermissionsNode != null)) {
                    writer.WriteComment("Code Access Security Permissions");
                    WriteCasPermissions(writer);
                }

                writer.WriteEndElement(); // </Solution>
                writer.WriteEndDocument();
                writer.Close();
                manifestStream.Close();
            }

        }

        static void WriteFeatureManifestFile(XmlWriter writer, string path) {
            writer.WriteStartElement("FeatureManifest");
            writer.WriteAttributeString("Location", path);
            writer.WriteEndElement();
        }

        static void WriteTemplateFiles(XmlWriter writer) {
            EnumerateTemplateDirectory(writer, Globals.TemplatesFolder);
        }

        static void EnumerateTemplateDirectory(XmlWriter writer, string path) {

            foreach (string file in Directory.GetFiles(path)) {

                // Addition 080612 wictor - ignore hidden or source control files
                if (isValidManifestFile(file)) {

                    if (!IsAFeatureFile(file)) {

                        writer.WriteStartElement("TemplateFile");
                        writer.WriteAttributeString("Location", file.Substring((Globals.TemplatesFolder + @"\").Length));
                        writer.WriteEndElement();
                    }
                }
            }

            foreach (string folder in Directory.GetDirectories(path)) {
                // Addition 080612 wictor - ignore hidden folders
                if (isValidDirectory(folder)) {
                    EnumerateTemplateDirectory(writer, folder);
                }
            }
        }

        public static bool IsAFeatureFile(string file) {
            // Modification 20090715 - bphillips - Changed to exclude feature files from the TemplateFiles node.
            if (file.ToLower().Contains(@"rootfiles\template\features")) {
                return true;
            }

            if (file.EndsWith(SolutionBuilder.PrimaryFeatureName)) return true;
            foreach (string f in SolutionBuilder.SecondaryFeatures) {
                if (file.EndsWith(f))
                    return true;
            }
            return false;
        }

        static void WriteRootFiles(XmlWriter writer) {
            EnumerateRootFilesDirectory(writer, Globals.RootFilesFolder);
        }

        static void EnumerateRootFilesDirectory(XmlWriter writer, string path) {

            foreach (string file in Directory.GetFiles(path)) {

                // Addition 080612 wictor - ignore hidden or source control files
                if (isValidManifestFile(file)) {

                    if (!file.Contains("TEMPLATE")) {

                        writer.WriteStartElement("RootFile");
                        writer.WriteAttributeString("Location", file.Substring((Globals.RootFilesFolder + @"\").Length));
                        writer.WriteEndElement();
                    }
                }
            }

            foreach (string folder in Directory.GetDirectories(path)) {
                // Addition 080612 wictor - ignore hidden folders
                if (isValidDirectory(folder)) {
                    if (!folder.Contains("TEMPLATE")) {
                        EnumerateRootFilesDirectory(writer, folder);
                    }
                }
            }
        }

        static void WriteAssembly(XmlWriter writer, SolutionAssembly asm) {
            writer.WriteStartElement("Assembly");
            writer.WriteStartAttribute("Location");
            writer.WriteValue(asm.Location);
            writer.WriteEndAttribute();
            writer.WriteStartAttribute("DeploymentTarget");
            writer.WriteValue(asm.DeploymentTarget);
            writer.WriteEndAttribute();
            if (SolutionBuilder.SafeControlSettings &&
                asm.SafeControlEntries.Count > 0) {
                writer.WriteStartElement("SafeControls");
                foreach (SolutionSafeControlEntry entry in asm.SafeControlEntries) {
                    writer.WriteStartElement("SafeControl");
                    writer.WriteStartAttribute("Assembly");
                    writer.WriteValue(entry.Assembly);
                    writer.WriteEndAttribute();
                    writer.WriteStartAttribute("Namespace");
                    writer.WriteValue(entry.Namespace);
                    writer.WriteEndAttribute();
                    writer.WriteStartAttribute("TypeName");
                    writer.WriteValue(entry.TypeName);
                    writer.WriteEndAttribute();
                    writer.WriteStartAttribute("Safe");
                    writer.WriteValue(entry.Safe);
                    writer.WriteEndAttribute();
                    writer.WriteEndElement(); // </SafeControl>
                }
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }

        static void WriteCasPermissions(XmlWriter writer) {
            if (SolutionBuilder.CasPermissionsNode == null) {
                Console.WriteLine("Solution has no CAS Permissions in SolutionConfig.xml");
                return;
            }
            writer.WriteStartElement("CodeAccessSecurity");
            writer.WriteStartElement("PolicyItem");
            writer.WriteStartElement("PermissionSet");
            writer.WriteAttributeString("class", "NamedPermissionSet");
            writer.WriteAttributeString("version", "1");
            writer.WriteAttributeString("Name", SolutionBuilder.TargetName);
            // write out each CAS permission as an IPermission element
            foreach (XmlNode child in SolutionBuilder.CasPermissionsNode) {
                writer.WriteStartElement("IPermission");
                foreach (XmlAttribute attr in child.Attributes) {
                    writer.WriteAttributeString(attr.Name, attr.Value);
                }
                writer.WriteEndElement(); // </IPermission>
            }
            writer.WriteEndElement(); // </PermissionSet>

            writer.WriteStartElement("Assemblies");
            writer.WriteStartElement("Assembly");
            writer.WriteAttributeString("Name", SolutionBuilder.TargetName);
            writer.WriteEndElement(); // </Assembly>
            writer.WriteEndElement(); // </Assemblies>
            writer.WriteEndElement(); // </PolicyItem>
            writer.WriteEndElement(); // </CodeAccessSecurity>      
        }


        // Added 080612 wictor - Added the methods
        /// <summary>
        /// Checks if the file is valid for inclusion in the Manifest file
        /// </summary>
        /// <param name="file">Path to the file to check</param>
        /// <returns>Returns false if the file is hidden or have an extension of .scc, otherwise false</returns>
        private static bool isValidManifestFile(string file) {
            FileInfo fileInfo = new FileInfo(file);
            // Modified 20090403 - bphillips - Ignore code files.
            return !(((fileInfo.Attributes & FileAttributes.Hidden) != 0)
                || (fileInfo.Extension.ToLower() == ".scc")
                || (fileInfo.Extension.ToLower() == ".cs")
                || (fileInfo.Extension.ToLower() == ".vb"));
        }

        private static bool isValidDirectory(string path) {
            DirectoryInfo directoryInfo = new DirectoryInfo(path);
            return !((directoryInfo.Attributes & FileAttributes.Hidden) != 0);
        }
        // End 080612 Addition

        // Modified 20090722 - bphillips - Changed to search FEATURES folder for feature.xml files.
        static void WriteFeatureManifestFiles(XmlWriter writer) {
            foreach (string file in Directory.GetFiles(Globals.FeaturesFolder, "*.xml", SearchOption.AllDirectories)) {
                if (Path.GetFileName(file).ToLower() == "feature.xml") {
                    FileInfo featureFile = new FileInfo(file);
                    WriteFeatureManifestFile(writer, featureFile.Directory.Name + @"\feature.xml");
                }
            }
        }
        // End of Modified 20090722 
    }



}
